"""
Anachronism Detection System for Temporal Dialogue Validation.

This module implements real-time detection of temporal inconsistencies,
knowledge boundary validation, and automatic correction suggestions.
"""

import numpy as np
import re
import logging
from typing import Dict, List, Optional, Tuple, Set, Union, Any
from dataclasses import dataclass, field
from enum import Enum
import asyncio
from collections import defaultdict
import json

logger = logging.getLogger(__name__)


class AnachronismType(Enum):
    """Types of anachronisms that can be detected."""
    TECHNOLOGICAL = "technological"
    LINGUISTIC = "linguistic"
    CULTURAL = "cultural"
    CONCEPTUAL = "conceptual"
    FACTUAL = "factual"
    SOCIAL = "social"


@dataclass
class AnachronismMatch:
    """Represents a detected anachronism."""
    text: str
    anachronism_type: AnachronismType
    time_period_first_known: int
    context_time_period: int
    confidence: float
    severity: float  # 0.0 (minor) to 1.0 (severe)
    position: Tuple[int, int]  # (start, end) character positions
    explanation: str
    suggested_alternatives: List[str] = field(default_factory=list)


@dataclass
class KnowledgeBoundary:
    """Defines knowledge boundaries for historical periods."""
    period_start: int
    period_end: int
    known_concepts: Set[str]
    unknown_concepts: Set[str]
    emerging_concepts: Set[str]
    technological_level: Dict[str, float]
    linguistic_patterns: Dict[str, float]
    cultural_norms: Dict[str, Any]


@dataclass
class AnachronismResult:
    """Result of anachronism detection."""
    detected: bool
    matches: List[AnachronismMatch]
    confidence: float
    overall_severity: float
    corrections: List[Dict[str, str]]
    validation_time_ms: float
    metadata: Dict[str, Any] = field(default_factory=dict)


class AnachronismDetector:
    """
    Real-time anachronism detection system.
    
    Features:
    - Temporal inconsistency detection across multiple domains
    - Knowledge boundary validation for historical periods
    - Automatic correction suggestions
    - Historical fact verification against authoritative sources
    """
    
    def __init__(self, knowledge_base_path: Optional[str] = None):
        """
        Initialize anachronism detector.
        
        Args:
            knowledge_base_path: Path to historical knowledge base
        """
        self.knowledge_boundaries = {}
        self.anachronism_patterns = self._initialize_anachronism_patterns()
        self.technological_timeline = self._initialize_technological_timeline()
        self.linguistic_evolution = self._initialize_linguistic_evolution()
        self.cultural_timeline = self._initialize_cultural_timeline()
        
        # Performance tracking
        self.detection_stats = {
            'total_detections': 0,
            'true_positives': 0,
            'false_positives': 0,
            'average_detection_time': 0.0,
            'detections_by_type': defaultdict(int)
        }
        
        if knowledge_base_path:
            self._load_knowledge_base(knowledge_base_path)
        
        logger.info("AnachronismDetector initialized")
    
    def _initialize_anachronism_patterns(self) -> Dict[AnachronismType, List[Dict]]:
        """Initialize patterns for different types of anachronisms."""
        return {
            AnachronismType.TECHNOLOGICAL: [
                {
                    'pattern': r'\b(computer|internet|smartphone|television|airplane|automobile|electricity|telephone|radio)\b',
                    'earliest_period': {
                        'computer': 1940,
                        'internet': 1969,
                        'smartphone': 1992,
                        'television': 1927,
                        'airplane': 1903,
                        'automobile': 1886,
                        'electricity': 1879,
                        'telephone': 1876,
                        'radio': 1895
                    },
                    'severity': 0.9
                },
                {
                    'pattern': r'\b(steam engine|printing press|gunpowder|compass|paper)\b',
                    'earliest_period': {
                        'steam engine': 1712,
                        'printing press': 1440,
                        'gunpowder': 1000,
                        'compass': 1100,
                        'paper': 105
                    },
                    'severity': 0.8
                }
            ],
            AnachronismType.LINGUISTIC: [
                {
                    'pattern': r'\b(okay|OK|cool|awesome|dude|guy|folks)\b',
                    'earliest_period': {
                        'okay': 1839,
                        'OK': 1839,
                        'cool': 1933,
                        'awesome': 1980,
                        'dude': 1883,
                        'guy': 1847,
                        'folks': 1890
                    },
                    'severity': 0.6
                },
                {
                    'pattern': r'\b(thou|thee|thy|hast|dost|prithee|forsooth)\b',
                    'period_range': (1066, 1700),
                    'severity': 0.7,
                    'context': 'archaic_english'
                }
            ],
            AnachronismType.CULTURAL: [
                {
                    'pattern': r'\b(democracy|human rights|equality|feminism|capitalism|socialism)\b',
                    'earliest_period': {
                        'democracy': -508,  # Ancient Athens
                        'human rights': 1689,
                        'equality': 1776,
                        'feminism': 1837,
                        'capitalism': 1776,
                        'socialism': 1832
                    },
                    'severity': 0.8
                }
            ],
            AnachronismType.CONCEPTUAL: [
                {
                    'pattern': r'\b(atom|molecule|bacteria|virus|DNA|gene|evolution|gravity)\b',
                    'earliest_period': {
                        'atom': 1803,
                        'molecule': 1811,
                        'bacteria': 1676,
                        'virus': 1892,
                        'DNA': 1869,
                        'gene': 1909,
                        'evolution': 1859,
                        'gravity': 1687
                    },
                    'severity': 0.9
                }
            ]
        }
    
    def _initialize_technological_timeline(self) -> Dict[str, Dict]:
        """Initialize timeline of technological developments."""
        return {
            'agriculture': {'period': -10000, 'region': 'mesopotamia'},
            'writing': {'period': -3200, 'region': 'mesopotamia'},
            'wheel': {'period': -3500, 'region': 'mesopotamia'},
            'iron_working': {'period': -1500, 'region': 'anatolia'},
            'printing_press': {'period': 1440, 'region': 'europe'},
            'steam_engine': {'period': 1712, 'region': 'europe'},
            'electricity': {'period': 1879, 'region': 'usa'},
            'internal_combustion': {'period': 1860, 'region': 'europe'},
            'flight': {'period': 1903, 'region': 'usa'},
            'radio': {'period': 1895, 'region': 'europe'},
            'television': {'period': 1927, 'region': 'usa'},
            'computer': {'period': 1940, 'region': 'usa'},
            'internet': {'period': 1969, 'region': 'usa'},
            'mobile_phone': {'period': 1973, 'region': 'usa'},
            'world_wide_web': {'period': 1989, 'region': 'europe'}
        }
    
    def _initialize_linguistic_evolution(self) -> Dict[str, Dict]:
        """Initialize patterns of linguistic evolution."""
        return {
            'old_english': {'period': (450, 1150), 'characteristics': ['þ', 'ð', 'æ']},
            'middle_english': {'period': (1150, 1500), 'characteristics': ['thou', 'thee', 'thy']},
            'early_modern_english': {'period': (1500, 1800), 'characteristics': ['doth', 'hath', 'ye']},
            'modern_english': {'period': (1800, 2000), 'characteristics': ['standardized_spelling']},
            'slang_evolution': {
                'cool_meaning_good': 1933,
                'awesome_meaning_good': 1980,
                'gay_meaning_homosexual': 1935,
                'computer_meaning_machine': 1940
            }
        }
    
    def _initialize_cultural_timeline(self) -> Dict[str, Dict]:
        """Initialize timeline of cultural and social developments."""
        return {
            'feudalism': {'period': (800, 1500), 'regions': ['europe']},
            'renaissance': {'period': (1300, 1600), 'regions': ['europe']},
            'enlightenment': {'period': (1650, 1800), 'regions': ['europe']},
            'industrial_revolution': {'period': (1760, 1840), 'regions': ['europe', 'usa']},
            'womens_suffrage': {'period': (1848, 1920), 'regions': ['global']},
            'civil_rights': {'period': (1954, 1968), 'regions': ['usa']},
            'social_concepts': {
                'nationalism': 1780,
                'romanticism': 1800,
                'modernism': 1890,
                'postmodernism': 1950
            }
        }
    
    def _load_knowledge_base(self, path: str):
        """Load external knowledge base for fact verification."""
        try:
            with open(path, 'r', encoding='utf-8') as f:
                knowledge_data = json.load(f)
            
            # Process knowledge base data
            self._process_knowledge_base(knowledge_data)
            logger.info(f"Loaded knowledge base from {path}")
            
        except Exception as e:
            logger.warning(f"Failed to load knowledge base from {path}: {e}")
    
    def _process_knowledge_base(self, data: Dict):
        """Process and index knowledge base data."""
        # This would process the knowledge base and create indexed lookups
        pass
    
    async def detect_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> AnachronismResult:
        """
        Detect anachronisms in the given text.
        
        Args:
            text: Text to analyze for anachronisms
            context: Dialogue context with time period information
            
        Returns:
            Detection result with identified anachronisms
        """
        import time
        start_time = time.time()
        
        matches = []
        
        # Run different detection methods in parallel
        detection_tasks = [
            self._detect_technological_anachronisms(text, context),
            self._detect_linguistic_anachronisms(text, context),
            self._detect_cultural_anachronisms(text, context),
            self._detect_conceptual_anachronisms(text, context),
            self._detect_factual_anachronisms(text, context)
        ]
        
        detection_results = await asyncio.gather(*detection_tasks)
        
        # Combine all matches
        for result in detection_results:
            matches.extend(result)
        
        # Remove duplicates and rank by severity
        matches = self._deduplicate_matches(matches)
        matches.sort(key=lambda x: x.severity, reverse=True)
        
        # Calculate overall metrics
        detected = len(matches) > 0
        confidence = self._calculate_detection_confidence(matches)
        overall_severity = max([m.severity for m in matches]) if matches else 0.0
        
        # Generate corrections
        corrections = await self._generate_corrections(matches, text, context)
        
        detection_time = (time.time() - start_time) * 1000
        
        # Update statistics
        self._update_detection_stats(matches, detection_time)
        
        return AnachronismResult(
            detected=detected,
            matches=matches,
            confidence=confidence,
            overall_severity=overall_severity,
            corrections=corrections,
            validation_time_ms=detection_time,
            metadata={
                'context_period': f"{context.time_period_start}-{context.time_period_end}",
                'detection_methods_used': len(detection_tasks),
                'total_matches_found': len(matches)
            }
        )
    
    async def _detect_technological_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> List[AnachronismMatch]:
        """Detect technological anachronisms."""
        matches = []
        
        for pattern_info in self.anachronism_patterns[AnachronismType.TECHNOLOGICAL]:
            pattern = pattern_info['pattern']
            earliest_periods = pattern_info['earliest_period']
            severity = pattern_info['severity']
            
            for match in re.finditer(pattern, text, re.IGNORECASE):
                word = match.group().lower()
                
                if word in earliest_periods:
                    first_known = earliest_periods[word]
                    
                    # Check if this technology existed in the context period
                    if first_known > context.time_period_end:
                        anachronism = AnachronismMatch(
                            text=match.group(),
                            anachronism_type=AnachronismType.TECHNOLOGICAL,
                            time_period_first_known=first_known,
                            context_time_period=context.time_period_start,
                            confidence=0.9,
                            severity=severity,
                            position=(match.start(), match.end()),
                            explanation=f"'{word}' was not invented until {first_known}",
                            suggested_alternatives=self._get_tech_alternatives(
                                word, context.time_period_start
                            )
                        )
                        matches.append(anachronism)
        
        return matches
    
    async def _detect_linguistic_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> List[AnachronismMatch]:
        """Detect linguistic anachronisms."""
        matches = []
        
        for pattern_info in self.anachronism_patterns[AnachronismType.LINGUISTIC]:
            pattern = pattern_info['pattern']
            
            if 'earliest_period' in pattern_info:
                earliest_periods = pattern_info['earliest_period']
                
                for match in re.finditer(pattern, text, re.IGNORECASE):
                    word = match.group().lower()
                    
                    if word in earliest_periods:
                        first_known = earliest_periods[word]
                        
                        if first_known > context.time_period_end:
                            anachronism = AnachronismMatch(
                                text=match.group(),
                                anachronism_type=AnachronismType.LINGUISTIC,
                                time_period_first_known=first_known,
                                context_time_period=context.time_period_start,
                                confidence=0.8,
                                severity=pattern_info['severity'],
                                position=(match.start(), match.end()),
                                explanation=f"'{word}' was not used in this sense until {first_known}",
                                suggested_alternatives=self._get_linguistic_alternatives(
                                    word, context.time_period_start
                                )
                            )
                            matches.append(anachronism)
            
            elif 'period_range' in pattern_info:
                period_start, period_end = pattern_info['period_range']
                
                # Check if archaic language is used outside its period
                if not (period_start <= context.time_period_start <= period_end):
                    for match in re.finditer(pattern, text, re.IGNORECASE):
                        word = match.group().lower()
                        
                        anachronism = AnachronismMatch(
                            text=match.group(),
                            anachronism_type=AnachronismType.LINGUISTIC,
                            time_period_first_known=period_start,
                            context_time_period=context.time_period_start,
                            confidence=0.7,
                            severity=pattern_info['severity'],
                            position=(match.start(), match.end()),
                            explanation=f"'{word}' is archaic and inappropriate for {context.time_period_start}",
                            suggested_alternatives=self._get_modern_alternatives(word)
                        )
                        matches.append(anachronism)
        
        return matches
    
    async def _detect_cultural_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> List[AnachronismMatch]:
        """Detect cultural and social anachronisms."""
        matches = []
        
        for pattern_info in self.anachronism_patterns[AnachronismType.CULTURAL]:
            pattern = pattern_info['pattern']
            earliest_periods = pattern_info['earliest_period']
            
            for match in re.finditer(pattern, text, re.IGNORECASE):
                concept = match.group().lower()
                
                if concept in earliest_periods:
                    first_known = earliest_periods[concept]
                    
                    if first_known > context.time_period_end:
                        anachronism = AnachronismMatch(
                            text=match.group(),
                            anachronism_type=AnachronismType.CULTURAL,
                            time_period_first_known=first_known,
                            context_time_period=context.time_period_start,
                            confidence=0.85,
                            severity=pattern_info['severity'],
                            position=(match.start(), match.end()),
                            explanation=f"Concept of '{concept}' did not emerge until {first_known}",
                            suggested_alternatives=self._get_cultural_alternatives(
                                concept, context.time_period_start
                            )
                        )
                        matches.append(anachronism)
        
        return matches
    
    async def _detect_conceptual_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> List[AnachronismMatch]:
        """Detect scientific and conceptual anachronisms."""
        matches = []
        
        for pattern_info in self.anachronism_patterns[AnachronismType.CONCEPTUAL]:
            pattern = pattern_info['pattern']
            earliest_periods = pattern_info['earliest_period']
            
            for match in re.finditer(pattern, text, re.IGNORECASE):
                concept = match.group().lower()
                
                if concept in earliest_periods:
                    first_known = earliest_periods[concept]
                    
                    if first_known > context.time_period_end:
                        anachronism = AnachronismMatch(
                            text=match.group(),
                            anachronism_type=AnachronismType.CONCEPTUAL,
                            time_period_first_known=first_known,
                            context_time_period=context.time_period_start,
                            confidence=0.9,
                            severity=pattern_info['severity'],
                            position=(match.start(), match.end()),
                            explanation=f"Scientific concept '{concept}' was not known until {first_known}",
                            suggested_alternatives=self._get_conceptual_alternatives(
                                concept, context.time_period_start
                            )
                        )
                        matches.append(anachronism)
        
        return matches
    
    async def _detect_factual_anachronisms(
        self,
        text: str,
        context: 'DialogueContext'
    ) -> List[AnachronismMatch]:
        """Detect factual anachronisms using historical knowledge base."""
        matches = []
        
        # This would implement fact-checking against authoritative sources
        # For now, return empty list as this requires external knowledge base
        
        return matches
    
    def _deduplicate_matches(self, matches: List[AnachronismMatch]) -> List[AnachronismMatch]:
        """Remove duplicate anachronism matches."""
        seen = set()
        unique_matches = []
        
        for match in matches:
            key = (match.text, match.position, match.anachronism_type)
            if key not in seen:
                seen.add(key)
                unique_matches.append(match)
        
        return unique_matches
    
    def _calculate_detection_confidence(self, matches: List[AnachronismMatch]) -> float:
        """Calculate overall confidence in detection results."""
        if not matches:
            return 1.0  # High confidence when no anachronisms found
        
        # Average confidence of individual matches
        avg_confidence = sum(m.confidence for m in matches) / len(matches)
        
        # Adjust based on number of matches (more matches = higher confidence)
        match_factor = min(1.0, len(matches) / 10.0)
        
        return min(avg_confidence + match_factor * 0.1, 1.0)
    
    async def _generate_corrections(
        self,
        matches: List[AnachronismMatch],
        original_text: str,
        context: 'DialogueContext'
    ) -> List[Dict[str, str]]:
        """Generate correction suggestions for detected anachronisms."""
        corrections = []
        
        for match in matches:
            if match.suggested_alternatives:
                # Use first alternative as primary suggestion
                primary_alternative = match.suggested_alternatives[0]
                
                corrections.append({
                    'type': 'replacement',
                    'original': match.text,
                    'replacement': primary_alternative,
                    'explanation': match.explanation,
                    'confidence': match.confidence,
                    'position': match.position
                })
            else:
                # Suggest regeneration if no direct alternative
                corrections.append({
                    'type': 'regeneration',
                    'original': match.text,
                    'explanation': match.explanation,
                    'constraints': {
                        'avoid_terms': [match.text.lower()],
                        'time_period': context.time_period_start,
                        'anachronism_type': match.anachronism_type.value
                    }
                })
        
        return corrections
    
    def _get_tech_alternatives(self, technology: str, time_period: int) -> List[str]:
        """Get period-appropriate alternatives for anachronistic technology."""
        alternatives = {
            'computer': ['calculating machine', 'abacus', 'slide rule'],
            'internet': ['telegraph', 'postal service', 'messenger'],
            'telephone': ['letter', 'messenger', 'telegraph'],
            'automobile': ['horse and carriage', 'walking', 'carriage'],
            'airplane': ['ship', 'horse', 'carriage'],
            'television': ['theater', 'books', 'storytelling'],
            'radio': ['town crier', 'bells', 'drums']
        }
        
        return alternatives.get(technology, ['period-appropriate equivalent'])
    
    def _get_linguistic_alternatives(self, word: str, time_period: int) -> List[str]:
        """Get period-appropriate alternatives for anachronistic language."""
        alternatives = {
            'okay': ['very well', 'yes', 'certainly'],
            'cool': ['excellent', 'fine', 'splendid'],
            'awesome': ['remarkable', 'wonderful', 'magnificent'],
            'dude': ['fellow', 'gentleman', 'sir'],
            'guy': ['man', 'fellow', 'gentleman']
        }
        
        return alternatives.get(word, ['period-appropriate term'])
    
    def _get_cultural_alternatives(self, concept: str, time_period: int) -> List[str]:
        """Get period-appropriate alternatives for anachronistic cultural concepts."""
        alternatives = {
            'democracy': ['republic', 'governance', 'rule'],
            'human rights': ['natural law', 'divine right', 'justice'],
            'equality': ['fairness', 'justice', 'balance'],
            'feminism': ['womens virtue', 'proper conduct', 'ladylike behavior']
        }
        
        return alternatives.get(concept, ['period-appropriate concept'])
    
    def _get_conceptual_alternatives(self, concept: str, time_period: int) -> List[str]:
        """Get period-appropriate alternatives for anachronistic scientific concepts."""
        alternatives = {
            'atom': ['indivisible particle', 'basic element', 'fundamental matter'],
            'bacteria': ['miasma', 'corruption', 'disease'],
            'virus': ['contagion', 'infection', 'sickness'],
            'evolution': ['divine creation', 'natural order', 'species'],
            'gravity': ['natural tendency', 'heavenly influence', 'attraction']
        }
        
        return alternatives.get(concept, ['period-appropriate understanding'])
    
    def _get_modern_alternatives(self, archaic_word: str) -> List[str]:
        """Get modern alternatives for archaic language."""
        alternatives = {
            'thou': ['you'],
            'thee': ['you'],
            'thy': ['your'],
            'hast': ['have'],
            'dost': ['do'],
            'prithee': ['please'],
            'forsooth': ['indeed', 'truly']
        }
        
        return alternatives.get(archaic_word, ['modern equivalent'])
    
    def _update_detection_stats(self, matches: List[AnachronismMatch], detection_time: float):
        """Update detection statistics."""
        self.detection_stats['total_detections'] += 1
        
        # Update running average detection time
        total = self.detection_stats['total_detections']
        self.detection_stats['average_detection_time'] = (
            (self.detection_stats['average_detection_time'] * (total - 1) + 
             detection_time) / total
        )
        
        # Count detections by type
        for match in matches:
            self.detection_stats['detections_by_type'][match.anachronism_type.value] += 1
    
    def validate_knowledge_boundary(
        self,
        concept: str,
        time_period: int,
        region: Optional[str] = None
    ) -> Tuple[bool, float, str]:
        """
        Validate if a concept is within knowledge boundaries for a time period.
        
        Args:
            concept: Concept to validate
            time_period: Historical time period
            region: Optional geographical region
            
        Returns:
            Tuple of (is_valid, confidence, explanation)
        """
        # Check technological timeline
        if concept in self.technological_timeline:
            tech_info = self.technological_timeline[concept]
            first_known = tech_info['period']
            
            if time_period < first_known:
                return (
                    False,
                    0.9,
                    f"Technology '{concept}' was not known until {first_known}"
                )
        
        # Check cultural timeline
        if concept in self.cultural_timeline.get('social_concepts', {}):
            first_known = self.cultural_timeline['social_concepts'][concept]
            
            if time_period < first_known:
                return (
                    False,
                    0.85,
                    f"Concept '{concept}' did not emerge until {first_known}"
                )
        
        return (True, 0.8, f"Concept '{concept}' is appropriate for {time_period}")
    
    def get_performance_metrics(self) -> Dict[str, Any]:
        """Get detector performance metrics."""
        total = self.detection_stats['total_detections']
        
        if total == 0:
            return self.detection_stats.copy()
        
        metrics = self.detection_stats.copy()
        
        # Calculate precision/recall if we have ground truth data
        if self.detection_stats['true_positives'] > 0:
            precision = (self.detection_stats['true_positives'] / 
                        (self.detection_stats['true_positives'] + 
                         self.detection_stats['false_positives']))
            metrics['precision'] = precision
        
        return metrics
    
    def add_custom_anachronism_pattern(
        self,
        anachronism_type: AnachronismType,
        pattern: str,
        earliest_period: Union[int, Dict[str, int]],
        severity: float = 0.8,
        alternatives: Optional[List[str]] = None
    ):
        """Add custom anachronism detection pattern."""
        pattern_info = {
            'pattern': pattern,
            'earliest_period': earliest_period,
            'severity': severity
        }
        
        if alternatives:
            pattern_info['alternatives'] = alternatives
        
        self.anachronism_patterns[anachronism_type].append(pattern_info)
        
        logger.info(f"Added custom {anachronism_type.value} pattern: {pattern}")
    
    def reset_stats(self):
        """Reset detection statistics."""
        self.detection_stats = {
            'total_detections': 0,
            'true_positives': 0,
            'false_positives': 0,
            'average_detection_time': 0.0,
            'detections_by_type': defaultdict(int)
        }
        logger.info("Detection statistics reset")